using System;
using System.Collections;
using System.Web;
using log4net;
using Spring.Collections;

namespace Sedna.Core.Web
{
	/// <summary>
	/// special HTTP module that is transparently registeres via root Web.config
	/// and works in pair with <c>DoubleSubmitStamp</c>. Their purpose is to 
	/// check if a submit button was clicked twice without proper waiting of a 
	/// form reload. This is done to prevent accidential duplication of not idempotent 
	/// business operations.
	/// </summary>
	public class DoublePostBackCheckHttpModule : IHttpModule
	{
		private ILog log = LogManager.GetLogger(typeof(DoublePostBackCheckHttpModule));
		private static SortedList stamps = new SortedList();

		private static int LOWER_THRESHOLD = 500;
		private static int UPPER_THRESHOLD = 1000;

		public static SortedList Stamps
		{
			get { return stamps; }
		}

		public static int LowerThreshold
		{
			get { return LOWER_THRESHOLD; }
		}

		public static int UpperThreshold
		{
			get { return UPPER_THRESHOLD; }
		}

		public void Init(HttpApplication context)
		{
			context.BeginRequest += new EventHandler(this.OnEnter);
		}


		public void Dispose()
		{
		}

		private void OnEnter(object sender, EventArgs args)
		{
			HttpApplication application = (HttpApplication)sender;
			string doubleSubmitStamp = application.Request.Params["dblsubmitstamp"];
			if(doubleSubmitStamp == null || string.Empty == doubleSubmitStamp)
			{
				return;
			}
			long stampValue = Int64.Parse(doubleSubmitStamp);
			lock(stamps.SyncRoot)
			{
				if(stamps.Contains(stampValue))
				{
					log.Debug("double submit detected");
					application.Response.Redirect("~/Core/Pages/DoubleSubmitError.aspx");
				}
				else
				{
					stamps.Add(stampValue, "");
				}

				CleanupStamps();
			}

		}

		public static void CleanupStamps()
		{
			if(stamps.Count < UPPER_THRESHOLD)
			{
				return;
			}

			SortedList newStamps = new SortedList();
			for(int i = stamps.Count - 1; i > LOWER_THRESHOLD; i--)
			{
				newStamps.Add(stamps.GetKey(i), "");
			}

			stamps.Clear();
			for(IDictionaryEnumerator en = newStamps.GetEnumerator(); en.MoveNext();)
			{
				stamps.Add(en.Key, en.Value);
			}
		}

	}
}
